#include <jni.h>
#include <stdlib.h>

#include "haptimap_types.h"
#include "haptimap_android_jni.h"
#include "common.h"

/*
 * Class:     org_haptimap_HaptiMap
 * Method:    GeoPointGet
 * Signature: (I[I[I[I[I)I
 */
jint Java_org_haptimap_HaptiMap_GeoPointGet(JNIEnv *env, jobject wrapper, 
                                            jint id, jintArray ptr_x, jintArray ptr_y, 
                                            jintArray ptr_z, jintArray ptr_is_3d)
{
    hm_t *hm = GET_HM(wrapper);
    int *x, *y, *z = NULL, *is_3d = NULL;
    HM_RESULT ret;

    MAP_INT(ptr_x, x);
    MAP_INT(ptr_y, y);
    if(ptr_z)
        MAP_INT(ptr_z, z);
    if(ptr_is_3d)
        MAP_INT(ptr_is_3d, is_3d);

    ret = hm_geo_point_get(hm, id, x, y, z, is_3d);

    UNMAP_INT(ptr_x, x);
    UNMAP_INT(ptr_y, y);
    if(ptr_z)
        UNMAP_INT(ptr_z, z);
    if(ptr_is_3d)
        UNMAP_INT(ptr_is_3d, is_3d);

    return ret;
}

/*
 * Class:     org_haptimap_HaptiMap
 * Method:    GeoLineStringDescribe
 * Signature: (I[I[I[I)I
 */
jint Java_org_haptimap_HaptiMap_GeoLineStringDescribe(JNIEnv *env, jobject wrapper, 
                                                      jint id, jintArray ptr_num_pts,
                                                      jintArray ptr_is_3d, jintArray ptr_is_polygon)
{
    hm_t *hm = GET_HM(wrapper);
    int *num_pts, *is_3d = NULL, *is_polygon = NULL;
    HM_RESULT ret;

    MAP_INT(ptr_num_pts, num_pts);
    if(ptr_is_3d)
        MAP_INT(ptr_is_3d, is_3d);
    if(ptr_is_polygon)
        MAP_INT(ptr_is_polygon, is_polygon);

    ret = hm_geo_linestring_describe(hm, id, num_pts, is_3d, is_polygon);

    UNMAP_INT(ptr_num_pts, num_pts);
    if(ptr_is_3d)
        UNMAP_INT(ptr_is_3d, is_3d);
    if(ptr_is_polygon)
        UNMAP_INT(ptr_is_polygon, is_polygon);

    return ret;
}

/*
 * Class:     org_haptimap_HaptiMap
 * Method:    GeoLineStringGet
 * Signature: (I[I[I[I)I
 */
jint Java_org_haptimap_HaptiMap_GeoLineStringGet(JNIEnv *env, jobject wrapper, jint id, 
                                                 jintArray x_arr, jintArray y_arr, jintArray z_arr)
{
    hm_t *hm = GET_HM(wrapper);
    int *x, *y, *z = NULL;
    HM_RESULT ret;

    MAP_INT(x_arr, x);
    MAP_INT(y_arr, y);
    if(z_arr)
        MAP_INT(z_arr, z);

    ret = hm_geo_linestring_get(hm, id, x, y, z, 1);

    UNMAP_INT(x_arr, x);
    UNMAP_INT(y_arr, y);
    if(z_arr)
        UNMAP_INT(z_arr, z);

    return ret;
}

/*
 * Class:     org_haptimap_HaptiMap
 * Method:    GeoLineStringGetPoint
 * Signature: (I[I[I[II)I
 */
jint Java_org_haptimap_HaptiMap_GeoLineStringGetPoint(JNIEnv *env, jobject wrapper, jint id, 
                                                      jintArray ptr_x, jintArray ptr_y, jintArray ptr_z, 
                                                      jint idx)
{
    hm_t *hm = GET_HM(wrapper);
    int *x, *y, *z = NULL;
    HM_RESULT ret;

    MAP_INT(ptr_x, x);
    MAP_INT(ptr_y, y);
    if(ptr_z)
        MAP_INT(ptr_z, z);

    ret = hm_geo_linestring_get_point(hm, id, &x, &y, &z, idx);

    UNMAP_INT(ptr_x, x);
    UNMAP_INT(ptr_y, y);
    if(ptr_z)
        UNMAP_INT(ptr_z, z);

    return ret;
}

/*
 * Class:     org_haptimap_HaptiMap
 * Method:    GeoAttributeGetId
 * Signature: (Lorg/haptimap/HaptiMap/GEOMETRY_TYPE;II[I)I
 */
jint Java_org_haptimap_HaptiMap_GeoAttributeGetId(JNIEnv *env, jobject wrapper, jobject gtype, 
                                                  jint feat_id, jint n, jintArray ptr_att_id)
{
    hm_t *hm = GET_HM(wrapper);
    int att_id;

    att_id = hm_geo_attribute_get_id(hm, GET_ORDINAL(gtype), feat_id, n);
    if(att_id < 0)
        return HM_FAILURE;

    STORE_INT(ptr_att_id, att_id);
    return HM_SUCCESS;
}

/*
 * Class:     org_haptimap_HaptiMap
 * Method:    GeoAttributeDescribe
 * Signature: (I[ILorg/haptimap/HaptiMap/ATTRIBUTE_TYPE;)I
 */
jint Java_org_haptimap_HaptiMap_GeoAttributeDescribe(JNIEnv *env, jobject wrapper, jint att_id, 
                                                     jintArray ptr_code, jintArray ptr_att_type)
{
    hm_t *hm = GET_HM(wrapper);
    int code, att_type_int;
    enum HM_ATTRIBUTE_TYPE att_type;

    if(hm_geo_attribute_describe(hm, att_id, &code, &att_type) != HM_SUCCESS)
        return HM_FAILURE;

    STORE_INT(ptr_code, code);
    /* Unfortunately there seems to be no easier way of retrieving an enum from
     * a JNI function than returning the ordinal value directly as here. */
    att_type_int = att_type;
    STORE_INT(ptr_att_type, att_type_int);

    return HM_SUCCESS;
}

/*
 * Class:     org_haptimap_HaptiMap
 * Method:    GeoAttributeGet
 * Signature: (I[I)I
 */
jint Java_org_haptimap_HaptiMap_GeoAttributeGet(JNIEnv *env, jobject wrapper, 
                                                jint att_id, jintArray ptr_val)
{
    hm_t *hm = GET_HM(wrapper);
    int *val;
    HM_RESULT ret;

    MAP_INT(ptr_val, val);
    ret = hm_geo_attribute_get(hm, att_id, val);
    UNMAP_INT(ptr_val, val);

    return ret;
}

/*
 * Class:     org_haptimap_HaptiMap
 * Method:    GeoAttributeGetMultiple
 * Signature: (I[I[I)I
 */
jint Java_org_haptimap_HaptiMap_GeoAttributeGetMultiple(JNIEnv *env, jobject wrapper, jint att_id, 
                                                        jintArray vals_arr, jintArray ptr_num_vals)
{
    hm_t *hm = GET_HM(wrapper);
    int *vals, *num_vals;
    HM_RESULT ret;

    MAP_INT(ptr_num_vals, num_vals);
    MAP_INT(vals_arr, vals);

    ret = hm_geo_attribute_get_multiple(hm, att_id, vals, num_vals);

    UNMAP_INT(ptr_num_vals, num_vals);
    UNMAP_INT(vals_arr, vals);

    return ret;
}

/*
 * Class:     org_haptimap_HaptiMap
 * Method:    GeoAttributeGetString
 * Signature: (ILjava/lang/StringBuilder;)I
 */
jint Java_org_haptimap_HaptiMap_GeoAttributeGetString(JNIEnv *env, jobject wrapper, 
                                                      jint att_id, jobject str_val)
{
    hm_t *hm = GET_HM(wrapper);
    const char *val;
    jstring str;

    if(hm_geo_attribute_get_string(hm, att_id, &val) != HM_SUCCESS)
        return HM_FAILURE;

    /* First convert C string to Java string */
    str = (*env)->NewStringUTF(env, val);
    /* Then append this Java string to the StringBuilder object */
    (*env)->CallObjectMethod(env, str_val, (*env)->GetMethodID(env, (*env)->GetObjectClass(env, str_val), 
                                                               "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;"),
                             str);

    return HM_SUCCESS;
}

/*
 * Class:     org_haptimap_HaptiMap
 * Method:    GeoListAll
 * Signature: (Lorg/haptimap/HaptiMap/GEOMETRY_TYPE;[I[I)I
 */
/* This is inefficient! */
jint Java_org_haptimap_HaptiMap_GeoListAll(JNIEnv *env, jobject wrapper, jobject gtype, 
                                           jintArray list_arr, jintArray ptr_num)
{
    hm_t *hm = GET_HM(wrapper);
    int *list, num, avail;

    if(hm_geo_list_all(hm, GET_ORDINAL(gtype), &list, &num) != HM_SUCCESS)
        return HM_FAILURE;

    (*env)->GetIntArrayRegion(env, ptr_num, 0, 1, &avail);
    STORE_INT(ptr_num, num);
    if(avail > num)
        avail = num;
    if(avail > 0)
        (*env)->SetIntArrayRegion(env, list_arr, 0, avail, list);
    hm_geo_list_free(hm, list);

    return HM_SUCCESS;
}
